import Logger from '../utils/Logger'
import inputEventClient from '@ohos.multimodalInput.inputEventClient';
import { Action as touchAction, ToolType as touchToolType, TouchEvent } from '@ohos.multimodalInput.touchEvent';
import inputMonitor from '@ohos.multimodalInput.inputMonitor';
import { Action as mouseAction, ToolType as mouseToolType } from '@ohos.multimodalInput.mouseEvent';
import { KeyCode } from '@ohos.multimodalInput.keyCode';
import { Axis, AxisValue, MouseEvent, Touch } from '@kit.InputKit';
import { promptAction } from '@kit.ArkUI';
import AccessibilityUtils from './AccessibilityUtils';
import touchEvent from '@ohos.multimodalInput.touchEvent'
import { sleep } from '../utils/DateTimeUtil';

let mouseEventReplacer = ["action", "screenX", "screenY", "axes", "axis", "pressedButtons", "value"]
let touchEventReplacer = ["action", "actionTime", "pressedTime", "screenX", "screenY", "touches"]

export default class inputManager {
  lastTouchTime: number = 0
  mimicTouchEvents: TouchEvent[] = []
  mimicMouseEvents: MouseEvent[] = []

  constructor() {
    console.error("_: inputManager constructed")

  }

  classifyCharacter(char) {
    if (char === ' ') {
      return 'space';
    } else if (char >= 'a' && char <= 'z') {
      return 'lowercase';
    } else if (char >= 'A' && char <= 'Z') {
      return 'uppercase';
    } else if (char >= '0' && char <= '9') {
      return 'digit';
    } else if (/[\!\@\#\$\%\&\*\(\)\_\+\-\=\:\;\"\'\,\.\/\?]/.test(char)) {
      return "symbol"
    } else {
      return 'unknown character';
    }
  }

  async keyCodeInput(str: string) {
    let TAG = "_:_keyCodeInput str=" + str
    console.log(TAG, "entered")
    await sleep(300)
    this.keyCode(KeyCode.KEYCODE_MOVE_END)
    for (let i = 0; i < 100; i++) {
      this.keyCode(KeyCode.KEYCODE_DEL)
    }
    promptAction.showToast({
      message: str
    })
    await sleep(300)
    console.log(TAG, "about to input", str)
    let strList = str.split("")
    for (let char of strList) {
      let charType = this.classifyCharacter(char)
      let code = -1
      switch (charType) {
        case "space":
          code = KeyCode.KEYCODE_SPACE
          break
        case "digit":
          code = char.charCodeAt(0) - '0'.charCodeAt(0) + KeyCode.KEYCODE_NUMPAD_0
          break
        case "lowercase":
          code = char.charCodeAt(0) - 'a'.charCodeAt(0) + KeyCode.KEYCODE_A
          break
        case "uppercase":
          code = char.charCodeAt(0) - 'A'.charCodeAt(0) + KeyCode.KEYCODE_A
          break
      }
      console.log(TAG, char, charType, code)
      this.keyCode(code)
      await sleep(40)
    }
    await sleep(300)
    this.keyCode(KeyCode.KEYCODE_TAB)
  }

  keyCode(code, state = "ud") {

    let TAG = "_:_~~~~~~~~[keyReturn]"
    try {
      let keyeventdata: inputEventClient.KeyEventData
      if (state.includes("d")) {
        let backKeyDn: inputEventClient.KeyEvent = {
          isPressed: true,
          keyCode: Number(code),
          keyDownDuration: 0,
          isIntercepted: false
        };
        keyeventdata = {
          keyEvent: backKeyDn
        }
        inputEventClient.injectKeyEvent(keyeventdata);
      }
      if (state.includes("u")) {
        let backKeyUp: inputEventClient.KeyEvent = {
          isPressed: false,
          keyCode: Number(code),
          keyDownDuration: 0,
          isIntercepted: false
        };
        keyeventdata = {
          keyEvent: backKeyUp
        }
        inputEventClient.injectKeyEvent(keyeventdata);
      }
    } catch (error) {
      console.log(TAG, `Failed to inject KeyEvent, error: ${JSON.stringify(error, [`code`, `message`])}`);
    }
  }

  touchEvent(x, y, action: touchAction) {
    let TAG = "_:_touchEvent"
    try {
      let touchEvent: touchEvent.Touch = {
        id: 1,
        pressedTime: 1,
        screenX: Math.floor(x),
        screenY: Math.floor(y),
        windowX: Math.floor(x),
        windowY: Math.floor(y),
        pressure: 0,
        width: 0,
        height: 0,
        tiltX: 0,
        tiltY: 0,
        toolX: 0,
        toolY: 0,
        toolWidth: 0,
        toolHeight: 0,
        rawX: 0,
        rawY: 0,
        toolType: 0,
      }

      let touchEventUpData: touchEvent.TouchEvent = {
        action: action,
        sourceType: 0,
        touch: touchEvent,
        touches: [touchEvent],
        id: 0,
        deviceId: 0,
        actionTime: 0,
        screenId: 0,
        windowId: 0
      }
      let touchEventUp: inputEventClient.TouchEventData = {
        touchEvent: touchEventUpData
      }
      inputEventClient.injectTouchEvent(touchEventUp);
      console.warn(TAG, "injectTouchEvent", x, y, action)
    } catch (error) {
      console.log(TAG, `Failed to inject touchEvent, error: ${JSON.stringify(error, [`code`, `message`])}`);
    }

  }

  mouseEvent(x: number, y: number, action: mouseAction, axes: AxisValue[] = [], buttonlist = [0]) {
    let TAG = "_:_MOUSE EVENT"
    console.log(TAG, x, y, action)
    if (x < 0 || y < 0) {
      console.error(TAG, "invalid click pos", x, y)
    }
    try {
      let mouseButtonUpData: MouseEvent = {
        id: 0,
        deviceId: 1,
        actionTime: (new Date()).getTime(),
        screenId: 0,
        windowId: 0,
        action: action,
        screenX: x,
        screenY: y,
        windowX: x,
        windowY: y,
        rawDeltaX: 200,
        rawDeltaY: 200,
        button: 0,
        pressedButtons: buttonlist,
        axes: axes,
        pressedKeys: [0],
        ctrlKey: false,
        altKey: false,
        shiftKey: false,
        logoKey: false,
        fnKey: false,
        capsLock: false,
        numLock: false,
        scrollLock: false,
        toolType: 1,
      }
      let mouseButtonUp: inputEventClient.MouseEventData = {
        mouseEvent: mouseButtonUpData
      }
      console.warn(TAG, "injecting MouseEvent", JSON.stringify(mouseButtonUpData))
      inputEventClient.injectMouseEvent(mouseButtonUp);
    } catch (error) {
      console.error(TAG, ` Failed to inject MouseEvent, error: ${JSON.stringify(error, [`code`, `message`])}`);
    }
  }

  mouseScroll(x, y, mode = "vf") {
    console.log("_:_mouseScroll", x, y, mode)
    let axis: Axis
    let value: number
    if (mode.includes("v")) {
      axis = Axis.SCROLL_VERTICAL
    }
    if (mode.includes("h")) {
      axis = Axis.SCROLL_HORIZONTAL
    }
    if (mode.includes("d")) {
      value = 15
    }
    if (mode.includes("u")) {
      value = -15
    }
    let axes: AxisValue[] = [{
      axis: axis, value: value
    }]
    y += 100
    for (let i = 0; i < 10; i++) {
      this.mouseEvent(x, y, mouseAction.AXIS_BEGIN, axes, [])
      this.mouseEvent(x, y, mouseAction.AXIS_END, axes, [])
    }
  }

  async mouseClick(x: number, y: number) { //cooresponds to left,top; not top,left
    this.mouseEvent(x, y, mouseAction.MOVE)
    await sleep(700)
    this.mouseEvent(x, y, mouseAction.BUTTON_DOWN)
    await sleep(100)
    this.mouseEvent(x, y, mouseAction.BUTTON_UP)
    // await  sleep(100)
    // this.mouseEvent(x, y, mouseAction.BUTTON_DOWN)
    // await  sleep(100)
    // this.mouseEvent(x, y, mouseAction.BUTTON_UP)
  }

  touchClick(x, y) { //cooresponds to left,top; not top,left
    this.touchEvent(x, y, touchAction.DOWN)
    this.touchEvent(x, y, touchAction.UP)
  }

  async touchScroll(direction: string = 'vf', sx: number = 360, sy: number = 540, step: number = 10) {
    let ex = sx
    let ey = sy
    if (direction == "vf") {
      ey = sy - 500
    } else if (direction == "hf") {
      ex = sx - 300
    } else if (direction == "vb") {
      ey = sy + 500
    } else if (direction == "hb") {
      ex = sx + 300
    } else {
      ey = sy - 500
    }
    let TAG = "_:_touchScroll"
    this.touchEvent(sx, sy, touchAction.DOWN)
    let dist = Math.sqrt((ex - sx) * (ex - sx) + (ey - sy) * (ey - sy))
    let cx
    let cy
    for (let i = 0; i < dist; i += step) {
      cx = sx + (ex - sx) * i / dist
      cy = sy + (ey - sy) * i / dist
      if (cx >= 0 && cx <= 720 && cy >= 0 && cy <= 1280) {
        this.touchEvent(cx, cy, touchAction.MOVE)
      } else {
        console.error(TAG, "out of bound", cx, cy, "from call:", sx, sy, ex, ey, step)
        break
      }
    }
    for (let i = 0; i < 15; i++) {
      this.touchEvent(cx, cy, touchAction.MOVE)
    }
    this.touchEvent(cx, cy, touchAction.UP)
  }

  oneTouchRecord(event: TouchEvent) {
    this.mimicTouchEvents.push(event)
  }

  oneMouseRecord(event: MouseEvent) {
    this.mimicMouseEvents.push(event)
  }

  async mimicMouse() {
    let TAG = "_:_MINIC MOUSE"
    for (let i = 0; i < this.mimicMouseEvents.length; i++) {
      let event = this.mimicMouseEvents[i]
      event.toolType = mouseToolType.MOUSE
      if (i > 0) {
        await sleep((event.actionTime - this.mimicMouseEvents[i-1].actionTime) / 1000)
      }
      try {
        inputEventClient.injectMouseEvent({
          mouseEvent: event
        });
        console.warn(TAG, "inject mouse success", JSON.stringify(event))
      } catch (error) {
        console.error(TAG, `Failed to inject mouseEvent, error: ${JSON.stringify(error, [`code`, `message`])}`);
      }
    }
    this.mimicMouseEvents = []
  }

  async mimicTouch() {
    let TAG = "_:_MINIC TOUCH"
    for (let i = 0; i < this.mimicTouchEvents.length; i++) {
      let event = this.mimicTouchEvents[i]
      event.touch = event.touches[0]
      if (i > 0) {
        await sleep((event.actionTime - this.mimicTouchEvents[i-1].actionTime) / 1000)
      }
      if (event.action != touchAction.UP) {
        try {
          inputEventClient.injectTouchEvent({
            touchEvent: event
          });
          console.warn(TAG, "inject touch success", JSON.stringify(event))
        } catch (error) {
          console.error(TAG, `Failed to inject touchEvent, error: ${JSON.stringify(error, [`code`, `message`])}`);
        }
      } else {
        break
      }
    }
    let endX = this.mimicTouchEvents[this.mimicTouchEvents.length-1].touches[0].screenX
    let endY = this.mimicTouchEvents[this.mimicTouchEvents.length-1].touches[0].screenY
    for (let i = 0; i < 15; i++) {
      this.touchEvent(endX, endY, touchAction.MOVE)
    }
    this.touchEvent(endX, endY, touchAction.UP)
    this.mimicTouchEvents = []
  }

  TouchRecogAct() {
    let TAG = "_:_TouchRecogAct"
    let seqs: number[] = this.mimicTouchEvents.map((event) => {
      return event.action
    })
    let upIdx = seqs.findIndex(seq => seq === 3);
    if (upIdx !== -1) {
      seqs.splice(upIdx + 1, seqs.length - upIdx - 1);
    }
    console.log(TAG, "this is scroll")
    let start = seqs.find(seq => seq == 1)
    let end = seqs.find(seq => seq == 3)
    let startT: Touch = this.mimicTouchEvents[start].touches[0]
    let endT: Touch = this.mimicTouchEvents[end].touches[0]
    if (seqs.includes(2)) {
      let dir = ""
      if (Math.abs(startT.screenY - endT.screenY) > Math.abs(startT.screenX - endT.screenX)) {
        dir += "v"
      } else {
        dir += "h"
      }
      if (startT.screenX + startT.screenY < endT.screenX + endT.screenY) {
        dir += "b"
      } else {
        dir += "f"
      }
      this.touchScroll(dir, startT.screenX, endT.screenY)
    } else {
      console.log(TAG, "this is touch")
      this.touchClick(startT.screenX, startT.screenY)
    }
    console.log(TAG, seqs);
  }

  monitorMouse(callback: (event: MouseEvent) => void) {
    try {
      inputMonitor.on('mouse', (mouseEvent: MouseEvent) => {
        // console.log(`_:_Monitor mouse on success ${JSON.stringify(mouseEvent, mouseEventReplacer)}`);
        promptAction.showToast({
          message: "MOUSE", duration: 50
        })
        console.log("_:_oneRecord mouseEvent", JSON.stringify(mouseEvent))
        callback(mouseEvent)
        return false;
      });
    } catch (error) {
      console.log(`Monitor on failed, error: ${JSON.stringify(error, [`code`, `message`])}`);
    }
  }

  monitorTouch(callback: (event: TouchEvent) => void, NavX: number = 0, NavY: number = 0, NavW: number = 0,
    WinH: number = 0, debounce: number = 200000) {
    let TAG = "_:_[onTouch]"
    try {
      promptAction.showToast({
        message: "页面记录已开启"
      })
      inputMonitor.on("touch", (touchEvent: TouchEvent) => {
        if (touchEvent.actionTime - this.lastTouchTime > debounce &&
          !AccessibilityUtils.isPointInRect({
            left: NavX,
            top: NavY,
            width: NavW,
            height: WinH
          }, touchEvent.touches[0].screenX, touchEvent.touches[0].screenY)) {
          // promptAction.showToast({
          //   message: "TOUCH", duration: 50
          // })
          // console.warn("_:_oneRecord touchEvent", JSON.stringify(touchEvent))
          console.log("_:_oneRecord touchEvent", JSON.stringify(touchEvent, touchEventReplacer))
          callback(touchEvent)
        }
        this.lastTouchTime = touchEvent.actionTime
        return false;
      });
    } catch (error) {
      console.log(TAG, `Monitor on failed, error: ${JSON.stringify(error, [`code`, `message`])}`);
    }
  }

  OffMonitorTouch() {
    let TAG = "_:_[offTouch]"
    try {
      inputMonitor.off("touch")
      // promptAction.showToast({
      //   message: "页面记录已关闭"
      // })
    } catch (e) {
      console.log(TAG, "touch off monitor success")
    }
  }

  OffMonitorMouse() {
    let TAG = "_:_[offTouch]"
    try {
      inputMonitor.off("mouse")
      // promptAction.showToast({
      //   message: "鼠标记录已关闭"
      // })
    } catch (e) {
      console.log(TAG, "mouse off monitor success")
    }
  }
}